#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>

#include <linux/fs.h>
#include <linux/cdev.h>

#define VSER_MAJOR    256
#define VSER_MINOR    0
#define VSER_DEV_CNT  1
#define VSER_DEV_NAME "vser"

static struct cdev vsdev;  // 具体的字符设备，静态定义，也可通过cdev_alloc动态定义
static struct file_operations vser_ops = {  // 操作字符设备的一些方法
    .owner = THIS_MODULE,
};

static int __init vser_init(void)
{
    int ret;
    dev_t dev;

#if 1  // 静态分配设备号
    dev = MKDEV(VSER_MAJOR, VSER_MINOR);  // 将主设备号和次设备号合并成一个设备号
    ret = register_chrdev_region(dev, VSER_DEV_CNT, VSER_DEV_NAME);  // 将构造的设备号注册到内核中(表示占用)
#else  // 动态分配设备号
    ret = alloc_chrdev_region(&dev, VSER_MINOR, VSER_DEV_CNT, VSER_DEV_NAME);
#endif
    if (ret)
        goto reg_err;

    cdev_init(&vsdev, &vser_ops);  // 字符设备初始化，并绑定设备操作方法集合
    vsdev.owner = THIS_MODULE;

    ret = cdev_add(&vsdev, dev, VSER_DEV_CNT);  // 将设备添加到内核中的cdev_map散列表中
    if (ret)
        goto add_err;

    return 0;
add_err:
    unregister_chrdev_region(dev, VSER_DEV_CNT);  // 注销已注册的设备号
reg_err:
    return ret;
}

static void __exit vser_exit(void)
{
    dev_t dev;
    dev = MKDEV(VSER_MAJOR, VSER_MINOR);

    cdev_del(&vsdev);  // 根据cdev找到散列表中的probe，并进行删除
    unregister_chrdev_region(dev, VSER_DEV_CNT);  // 注销已注册的设备号
}

module_init(vser_init);
module_exit(vser_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("KDY <chaoxi_todo@qq.com>");
MODULE_DESCRIPTION("A simple module");
MODULE_ALIAS("virtual-serial");